package programa;

import java.awt.*;
import java.awt.event.KeyEvent;
import java.io.*;
import java.util.ArrayList;
import javax.swing.*;
import juego.*;
import movimiento.Posicion;
import objetos.aeronaves.Algo42;
import objetos.inanimados.Nube;
import org.jdom.*;
import org.jdom.input.SAXBuilder;
import org.jdom.output.*;
import vistas.*;
import ar.uba.fi.algo3.titiritero.*;
import controlador.*;
import controles.*;
import excepciones.*;

/*
 * Clase que se encarga ejecutar la secuencia para la continuidad de la partida, 
 * ya sea nueva o cargada.
 */
public class Partida {
	private static Partida instancia;
	private Jugador jugador;
	// private Nivel nivelActual = null;
	private int numeroDeNivel;
	private int cantidadDeNiveles = 4;
	private ControladorJuego controlador;
	private VentanaPrincipal ventana = null;
	private Records records;

	public static void main(String[] args) {
		Partida.getInstance();
	}

	public static Partida getInstance() {
		if (instancia == null) {
			instancia = new Partida();
		}
		return instancia;
	}

	private Partida() {
		this.numeroDeNivel = 1;
		controlador = new ControladorJuego(false);
		/* Agrego los controles */
		// agregarControlesAlControlador(controlador);
		/* Creo la ventana */
		if (ventana == null) {
			ventana = new VentanaPrincipal(controlador);
		}
		controlador.setSuperficieDeDibujo(ventana.getSuperficieDeDibujo());
		ventana.add(getPanelPrincipal());
		getPanelPrincipal().setVisible(true);
		ventana.setVisible(true);
		records = new Records();
	}

	public void nuevaPartida(String nombre) {
		Jugador jug = new Jugador(nombre);
		this.jugador = jug;
		prepararNivel();
		jugarNivel();
	}

	public boolean hayProximoNivel() {
		int proximoNivel = numeroDeNivel + 1;
		return proximoNivel < cantidadDeNiveles;
	}

	public void siguienteNivel() {
		if (!hayProximoNivel()) {
			finDeJuego();
		}
		numeroDeNivel += 1;
		prepararNivel();
		jugarNivel();
	}

	private void reiniciarControlador() {
		controlador.resetear();
	}

	private void prepararNivel() {
		Escenario.resetear();
		reiniciarControlador();
		/* Conecto con el escenario */
		Escenario.getInstance().setControlador(controlador);
		controlador.agregarDibujable(new VistaEscenario());
		agregarControlesAlControlador(controlador);
		agregarNubes(controlador, 10);
		/* Cargo los datos del nivel */
		try {
			Nivel.cargarNivel(numeroDeNivel);
		} catch (NivelNoEncontradoException e) {
			JOptionPane.showMessageDialog(ventana, "error al cargar el nivel");
			irAlMenuPrincipal();
		}
		/* Agrego el algo42 y el HUD */
		Algo42 algo42 = Partida.getInstance().getJugador().getAlgo42();
		Escenario.getInstance().setActor(algo42);
		if (algo42.getPosicion() == null) {
			int x = Escenario.getInstance().getMaxX() / 2;
			int y = Escenario.getInstance().getMaxY() - algo42.getTamanio() * 4;
			algo42.setPosicion(new Posicion(x, y));
		}
		agregarVistasDelHUD(controlador);
		/* Agrego controlador de teclado */
		KeyPressedObservador observador = new ControladorAlgo42(algo42);
		controlador.agregarKeyPressObservador(observador);
		observador = new ControladorContinuidad(controlador);
		controlador.agregarKeyPressObservador(observador);
		/* Preparo para que se comienze a jugar el nivel */
		PanelPrincipal.setVisible(false);
		if (panelFinNivel != null) {
			panelFinNivel.setVisible(false);
		}
		ventana.superficieDibujoVisible(true);
		controlador.setIntervaloSimulacion(15);
	}

	private void setJugador(Jugador jugadore) {
		this.jugador = jugadore;
	}

	public Jugador getJugador() {
		return this.jugador;
	}

	public VentanaPrincipal getVentana() {
		return ventana;
	}

	public void setVentana(VentanaPrincipal ventana) {
		this.ventana = ventana;
	}

	private void jugarNivel() {
		Escenario.getInstance().getControlador().comenzarJuegoAsyn();
	}

	public void finDeNivel(Boolean gano) {
		if (panelFinNivel != null) {
			panelFinNivel.setVisible(false);
		}
		this.jugador.modificarPuntaje(Escenario.getInstance().getActor()
				.getPuntaje());
		if (!gano) {
			numeroDeNivel = 1;
			records.nuevoRecord(jugador.getNombre(), jugador.getPuntaje());
		}
		ventana.superficieDibujoVisible(false);
		panelFinNivel = new VistaFinNivel(gano);
		panelFinNivel.setVisible(true);
		ventana.add(panelFinNivel);
		ventana.repaint();
	}

	public void finDeJuego() {
		numeroDeNivel = 1;
		irAlMenuPrincipal();
	}

	private void agregarControlesAlControlador(ControladorJuego controlador) {
		controlador.agregarControl(new ControlEntradaAeronaves());
		controlador.agregarControl(new ControlVivir());
		controlador.agregarControl(new ControlColisiones());
		controlador.agregarControl(new ControlRemoverDestruidos());
		controlador.agregarControl(new ControlEnemigos());
	}

	private void agregarVistasDelHUD(ControladorJuego controlador) {
		controlador.agregarDibujable(new VistaBarraVida(this.jugador
				.getAlgo42()));
		controlador.agregarDibujable(new VistaPuntaje());
		controlador.agregarDibujable(new VistaVidas(this.jugador.getAlgo42()));
		controlador.agregarDibujable(new VistaContadoresMunicion(this.jugador
				.getAlgo42()));
	}

	private void agregarNubes(ControladorJuego controlador, int cantidadNubes) {
		Nube nube;
		int x, y;
		VistaNube vista;
		for (int i = 0; i < cantidadNubes; i++) {
			x = (int) (Math.random() * Escenario.getInstance().getMaxX());
			y = (int) (Math.random() * (Escenario.getInstance().getMaxY() / -2));
			nube = new Nube(x, y);
			vista = new VistaNube(nube);
			controlador.agregarObjetoVivo(nube);
			controlador.agregarDibujable(vista);
		}
	}

	/* Persistencia */
	public void guardarPartida() throws ErrorAlCargarArchivo {
		/* Creamos el elemento root */
		Element root = new Element("partida");

		root.setAttribute(new Attribute("nivel", String
				.valueOf(this.numeroDeNivel)));
		root.addContent(this.jugador.obtenerNodo());

		root.addContent(Escenario.getInstance().obtenerNodo());

		/* Creamos el documento */
		Document doc = new Document(root);

		/* Lo almacenamos en un fichero dado por argumento */
		try {
			String nombreArchivo = "./partidasGuardadas/"
					+ this.jugador.getNombre() + ".xml";

			XMLOutputter salidaXml = new XMLOutputter(Format.getPrettyFormat());
			new File(nombreArchivo).createNewFile();
			FileOutputStream archivo = new FileOutputStream(nombreArchivo);
			salidaXml.output(doc, archivo);
			archivo.flush();
			archivo.close();
		} catch (Exception e) {
			throw new ErrorAlCargarArchivo("No se pudo guardar la partida");
		}

	}

	public boolean cargarPartida(String nombre) {
		String archivoACargar = "./partidasGuardadas/" + nombre + ".xml";
		SAXBuilder builder = new SAXBuilder();

		Document doc = null;
		Element root = null;

		try {
			doc = builder.build(new FileInputStream(archivoACargar));
		} catch (JDOMException e) {
			JOptionPane.showMessageDialog(ventana, "El archivo esta corrupto");
			this.jugador = null;
			nombreJugador = "";
			return false;
		} catch (IOException e) {
			JOptionPane
					.showMessageDialog(ventana,
							"Esta opcion no esta disponible de momento, intentelo de nuevo mas tarde");
			this.jugador = null;
			nombreJugador = "";
			return false;
		}

		root = doc.getRootElement();

		Partida.getInstance().setNumeroDeNivel(
				Integer.parseInt(root.getAttributeValue("nivel")));

		Partida.getInstance().setJugador(new Jugador(root.getChild("jugador")));
		prepararNivel();
		Escenario.getInstance().cargarEscenario(root.getChild("escenario"));

		PanelPrincipal.setVisible(false);

		Partida.getInstance().jugarNivel();
		return true;
	}

	public ArrayList<String> obtenerPartidasGuardadas() {
		ArrayList<String> listaNombresPartidasGuardadas = new ArrayList<String>();
		File directorio = new File("./partidasGuardadas");
		if (directorio.exists() && directorio.isDirectory()) {
			String[] nombresFicheros = directorio.list();
			for (String nombre : nombresFicheros) {
				if (!nombre.endsWith(".xml")) {
					continue;
				}
				String partidaGuardada = nombre.substring(0,
						nombre.length() - 4);
				listaNombresPartidasGuardadas.add(partidaGuardada);
				System.out.println(partidaGuardada);
			}
		} else {
			JOptionPane.showMessageDialog(ventana, "Archivos corruptos");
		}
		return listaNombresPartidasGuardadas;
	}

	private void setNumeroDeNivel(int nNivel) {
		this.numeroDeNivel = nNivel;
	}

	private JPanel PanelPrincipal = null;
	private JPanel panelRecords = null;
	private JPanel panelFinNivel = null;
	private JButton CargarPartida = null;
	private JButton VerRecords = null;
	private JButton Salida = null;
	private JButton Comenzar = null;
	private JButton cambiarPiloto = null;
	private JLabel mostrarPiloto = null;
	private JLabel fondo = null;
	private String nombreJugador = null;
	private int posX = 260;

	private JPanel getPanelPrincipal() {
		if (PanelPrincipal == null) {
			fondo = new JLabel();
			fondo.setBounds(new Rectangle(-3, 0, 720, 650));
			fondo.setIcon(new ImageIcon(getClass().getResource(
					"/images/fondo.jpg")));
			fondo.setBackground(Color.BLACK);
			mostrarPiloto = new JLabel();
			mostrarPiloto.setBounds(new Rectangle(posX, 163, 286, 40));
			mostrarPiloto.setFont(new Font("Monotype Corsiva", Font.PLAIN, 24));
			mostrarPiloto.setForeground(new Color(0, 102, 255));
			mostrarPiloto.setText("Piloto: <nuevo>");
			PanelPrincipal = new JPanel();
			PanelPrincipal.setLayout(null);
			PanelPrincipal.add(getComenzar(), null);
			PanelPrincipal.add(getCambiarPiloto(), null);
			PanelPrincipal.add(getCargarPartida(), null);
			PanelPrincipal.add(getVerRecords(), null);
			PanelPrincipal.add(getSalida(), null);
			PanelPrincipal.add(mostrarPiloto, null);
			PanelPrincipal.add(fondo, null);
		}
		return PanelPrincipal;
	}

	private JButton getComenzar() {
		if (Comenzar == null) {
			Comenzar = new JButton();
			Comenzar.setText("Nuevo piloto");
			Comenzar.setSize(new Dimension(160, 45));
			Comenzar.setLocation(new Point(posX, 255));
			Comenzar.addActionListener(new java.awt.event.ActionListener() {
				public void actionPerformed(java.awt.event.ActionEvent e) {
					if (nombreJugador == null) {
						nombreJugador = obtenerNombreUsuario();
						if (nombreJugador != null) {
							actualizarVista(nombreJugador);
						}
					} else {
						Partida.getInstance().nuevaPartida(nombreJugador);
					}
				}
			});
		}
		return Comenzar;
	}

	private JButton getCambiarPiloto() {
		if (cambiarPiloto == null) {
			cambiarPiloto = new JButton();
			cambiarPiloto.setText("Cambiar de piloto");
			cambiarPiloto.setSize(new Dimension(160, 30));
			cambiarPiloto.setLocation(new Point(posX, 320));
			cambiarPiloto
					.addActionListener(new java.awt.event.ActionListener() {
						public void actionPerformed(java.awt.event.ActionEvent e) {
							String nombre = obtenerNombreUsuario();
							if (nombre != null) {
								actualizarVista(nombre);
							}
						}
					});
		}
		return cambiarPiloto;
	}

	private JButton getCargarPartida() {
		if (CargarPartida == null) {
			CargarPartida = new JButton();
			CargarPartida.setMnemonic(KeyEvent.VK_UNDEFINED);
			CargarPartida.setLocation(new Point(posX, 370));
			CargarPartida.setSize(new Dimension(160, 30));
			CargarPartida.setText("Cargar partida");
			CargarPartida
					.addActionListener(new java.awt.event.ActionListener() {
						public void actionPerformed(java.awt.event.ActionEvent e) {
							new VistaCargarArchivo();
						}
					});
		}
		return CargarPartida;
	}

	private JButton getVerRecords() {
		if (VerRecords == null) {
			VerRecords = new JButton();
			VerRecords.setText("Ver records");
			VerRecords.setSize(new Dimension(160, 30));
			VerRecords.setLocation(new Point(posX, 420));
			VerRecords.addActionListener(new java.awt.event.ActionListener() {
				public void actionPerformed(java.awt.event.ActionEvent e) {
					panelRecords = new VistaRecords(records);
					ventana.add(panelRecords);
					PanelPrincipal.setVisible(false);
					panelRecords.setVisible(true);
				}
			});
		}
		return VerRecords;
	}

	private JButton getSalida() {
		if (Salida == null) {
			Salida = new JButton();
			Salida.setText("SALIR");
			Salida.setSize(new Dimension(160, 30));
			Salida.setLocation(new Point(posX, 500));
			Salida.addActionListener(new java.awt.event.ActionListener() {
				public void actionPerformed(java.awt.event.ActionEvent e) {
					if (confirmarSalida()) {
						System.exit(0);
					}
				}
			});
		}
		return Salida;
	}

	public String obtenerNombreUsuario() {
		String nombre = null;
		nombre = JOptionPane.showInputDialog(ventana,
				"Por favor ingrese su nombre");
		if (nombre == null) {
			/* El usuario cancela el ingreso del nombre */
		} else if (nombre.isEmpty()) {
			JOptionPane.showMessageDialog(ventana,
					"Debe ingresar su nombre para poder continuar");
			return null;
		} else {
			this.jugador = new Jugador(nombre);
		}
		return nombre;
	}

	public Boolean confirmarSalida() {
		try {
			records.guardarRecords();
		} catch (ErrorAlCargarArchivo e) {
			System.out.print(e.getMessage());
		}
		return JOptionPane.showConfirmDialog(ventana,
				"�Esta seguro que desea salir?") == 0;
	}

	public void irAlMenuPrincipal() {
		controlador.detenerJuego();
		ventana.superficieDibujoVisible(false);
		if (panelFinNivel != null) {
			panelFinNivel.setVisible(false);
		}
		if (panelRecords != null) {
			panelRecords.setVisible(false);
		}
		PanelPrincipal.repaint();
		PanelPrincipal.setVisible(true);
		ventana.repaint();
	}

	public void actualizarVista(String nombre) {
		mostrarPiloto.setText("Piloto: " + nombre);
		Comenzar.setText("A Volar!");
		nombreJugador = nombre;
	}

}
